package concurrent;

/**
 * @description: TODO 等待唤醒
 * @author: wzj
 * @create: 2019/3/25 8:52
 */
public class WaitNotify {
    /**
     * TODO synchronized (对象)和wait、notify的对象应该一致
     * TODO 否则会报java.lang.IllegalMonitorStateException，不合法的监视器错误
     */
    private ThisObject thisObject;

    public ThisObject getThisObject() {
        return thisObject;
    }

    public void setThisObject(ThisObject thisObject) {
        this.thisObject = thisObject;
    }

    /*
    TODO 使用ThisObject对象作为监视器
     */
    public void waitone1() throws InterruptedException {
        System.out.println("thisObject wait前 "+Thread.currentThread().getName());
        synchronized (thisObject){
            System.out.println("wait前 "+Thread.currentThread().getName());
            thisObject.wait();//使用了wait，会释放当前this对象的锁，就是不用等到synchronized (this){}这个代码块执行完，别的线程也能获取this对象锁，进入【线程相对应】的synchronized (this){}代码块了
            System.out.println("wait后 "+Thread.currentThread().getName());
        }
    }
    public void notifyone1() {
        System.out.println("thisObject notify前 "+Thread.currentThread().getName());
        synchronized (thisObject){//当没有其他线程占有this对象锁时，调用notifyone方法的线程才可以获取this对象锁，进入synchronized代码块
            System.out.println("notify前 "+Thread.currentThread().getName());
            thisObject.notify();//这一步还没有释放this对象的锁，只是唤醒了【某个】等待的线程。只有程序运行完synchronized (this){}代码块，才释放掉this对象的锁
            System.out.println("notify后 "+Thread.currentThread().getName());
        }
        //就是notifyone()运行到这里，被唤醒的线程才可以去抢this对象锁。【抢是因为除了被唤醒的线程外，可能还有其他没有调用waitone方法但也用了this对象锁的线程，所以谁抢到了谁用】
    }

    /*
    TODO 使用this，WaitNotify对象作为监视器
     */
    public void waitone() throws InterruptedException {
        System.out.println("this wait前 "+Thread.currentThread().getName());
        synchronized (this){
            System.out.println("wait前 "+Thread.currentThread().getName());
            this.wait();//使用了wait，会释放当前this对象的锁，就是不用等到synchronized (this){}这个代码块执行完，别的线程也能获取this对象锁，进入【线程相对应】的synchronized (this){}代码块了
            System.out.println("wait后 "+Thread.currentThread().getName());
        }
    }
    /**
     * 带有超时的wait，超时自动唤醒
     * @param timeout
     * @throws InterruptedException
     */
    public void waitone(long timeout) throws InterruptedException {
        System.out.println("this wait前 "+Thread.currentThread().getName());
        synchronized (this){
            System.out.println("wait前 "+Thread.currentThread().getName());
            this.wait(timeout);//使用了wait，会释放当前this对象的锁，就是不用等到synchronized (this){}这个代码块执行完，别的线程也能获取this对象锁，进入【线程相对应】的synchronized (this){}代码块了
            System.out.println("wait后 "+Thread.currentThread().getName());
        }
    }
    public void notifyone() {
        System.out.println("this notify前 "+Thread.currentThread().getName());
        synchronized (this){//当没有其他线程占有this对象锁时，调用notifyone方法的线程才可以获取this对象锁，进入synchronized代码块
            System.out.println("notify前 "+Thread.currentThread().getName());
            this.notify();//这一步还没有释放this对象的锁，只是唤醒了【某个】等待的线程。只有程序运行完synchronized (this){}代码块，才释放掉this对象的锁
            System.out.println("notify后 "+Thread.currentThread().getName());
        }
        //就是notifyone()运行到这里，被唤醒的线程才可以去抢this对象锁。【抢是因为除了被唤醒的线程外，可能还有其他没有调用waitone方法但也用了this对象锁的线程，所以谁抢到了谁用】
    }
    public void notifyall() {
        System.out.println("this notifyAll前 "+Thread.currentThread().getName());
        synchronized (this){//当没有其他线程占有this对象锁时，调用notifyall方法的线程才可以获取this对象锁，进入synchronized代码块
            System.out.println("notifyAll前 "+Thread.currentThread().getName());
            this.notifyAll();//这一步还没有释放this对象的锁，只是唤醒了【所有】等待的线程。只有程序运行完synchronized (this){}代码块，才释放掉this对象的锁
            System.out.println("notifyAll后 "+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        WaitNotify waitNotify=new WaitNotify();
        waitNotify.setThisObject(new ThisObject());
        for(int i=0;i<3;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        waitNotify.waitone();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        Thread.sleep(2000);//需要睡一下，因为有可能wait线程还没结束呢，就执行notify，那样notify就不起作用了
        waitNotify.notifyone();//随机唤醒一个线程
//        waitNotify.notifyall();//唤醒所有线程
    }
}

class ThisObject{}